"""
Custom Logger.

Authors: [airmind] and [Guillaume Algis] from stackoverflow.

Ref:
    1. https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output
"""
import logging
from pathlib import Path
from typing import Union
from datetime import datetime

BLACK, RED, GREEN, ORANGE, BLUE, MAGENTA, CYAN, GREY, WHITE = range(9)

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"

def formatter_message(message, use_color = True):
    if use_color:
        message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
    else:
        message = message.replace("$RESET", "").replace("$BOLD", "")
    return message

COLORS = {
    'WARNING': ORANGE,
    'INFO': GREEN,
    'DEBUG': WHITE,
    'CRITICAL': RED,
    'ERROR': RED
}


class ColoredFormatter(logging.Formatter):

    def __init__(self, msg, use_color = True):
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color

    def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in COLORS:
            levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
            record.levelname = levelname_color
        return logging.Formatter.format(self, record)


class ColoredLogger(logging.Logger):
    FORMAT = "[$BOLD%(name)-s$RESET][%(levelname)-s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
    COLOR_FORMAT = formatter_message(FORMAT, True)
    def __init__(self, name):
        logging.Logger.__init__(self, name)

        color_formatter = ColoredFormatter(self.COLOR_FORMAT)

        console = logging.StreamHandler()
        console.setFormatter(color_formatter)

        self.addHandler(console)
        return

    def add_csvfile(self, file: Union[str, Path]):
        """
        Add a csv file to the logger.

        Parameters:
        - file : Union[str, Path], required, the file path.
        """
        if self.file_handle is not None:
            self.file_handle.close()

        file = Path(file)
        file.parent.mkdir(exist_ok=True)
        self.file_handle = file.open("w")

    def log_csv(self, *msg, echo=False, log_time=True):
        """
        Log to the csv file.

        Parameters:
        - msg : tuple, required, the message to log;
        - echo : bool, optional, default: False, print to the console;
        - log_time : bool, optional, default: True, log the time.
        """
        if log_time:  # prefix with time
            now = datetime.now()
            text = now.strftime("%y/%m/%d %H:%M:%S, ") + f"{now.timestamp():.3f}, "
        else:
            text = ""

        for item in msg:
            text += str(item)
            text += ", "

        if echo:
            self.info(text)

        self.file_handle.write(text + "\n")

    def __del__(self):
        if self.file_handle is not None:
            self.file_handle.close()

    @staticmethod
    def get_logger(name: str) -> 'ColoredLogger':
        """
        Get the logger.

        Parameters:
        - name : str, required, the logger name.

        Returns:
        - logger : ColoredLogger, the logger.
        """
        logging.setLoggerClass(ColoredLogger)
        logger = logging.getLogger(name)
        return logger